#include "StreamListener.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

extern "C" {
#include <libswscale/swscale.h>
#include <x264.h>
}

static x264_param_t param;
static x264_t *encoder;
static x264_picture_t pic_in, pic_out;
static struct SwsContext* sws_context;

StreamListener::StreamListener(Ogre::RenderWindow *win, Ogre::Camera *cam, char *filename) {
  window = win;
  camera = cam;
  int width = window->getWidth();
  int height = window->getHeight();
  texture = Ogre::TextureManager::getSingleton().createManual(
     "StreamTex", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, Ogre::TEX_TYPE_2D,
     width, height, 0, Ogre::PF_B8G8R8A8, Ogre::TU_RENDERTARGET);

  Ogre::RenderTexture *renderTex = texture->getBuffer()->getRenderTarget();
  renderTex->addViewport(camera);

  Ogre::Viewport *viewport = renderTex->getViewport(0);
  viewport->setClearEveryFrame(true);
  viewport->setBackgroundColour(Ogre::ColourValue::Black);
  viewport->setOverlaysEnabled(true);

  renderTex->addListener(this);
  renderTex->setAutoUpdated(true);

  count = bytes = 0;
  fd = creat(filename, 0644);

  x264_param_default_preset(&param, "veryfast", "zerolatency");
  param.i_threads = 1;
  param.i_width = width;
  param.i_height = height;
  param.i_fps_num = 25;
  param.i_fps_den = 1;
  param.i_keyint_max = 25;
  // param.b_intra_refresh = 1;
  param.rc.i_rc_method = X264_RC_CRF;
  param.rc.f_rf_constant = 25;
  param.rc.f_rf_constant_max = 35;
  param.b_repeat_headers = 1;
  param.b_annexb = 1;
  x264_param_apply_profile(&param, "baseline");
  encoder = x264_encoder_open(&param);
  x264_picture_alloc(&pic_in, X264_CSP_I420, width, height);
  sws_context = sws_getContext(width, height, PIX_FMT_RGBA,
                               width, height, PIX_FMT_YUV420P,
                               SWS_FAST_BILINEAR, NULL, NULL, NULL);
}

void StreamListener::postRenderTargetUpdate(const Ogre::RenderTargetEvent &evt) {
  Ogre::HardwarePixelBufferSharedPtr buffer = texture->getBuffer();
  buffer->lock(Ogre::HardwareBuffer::HBL_READ_ONLY);
  const Ogre::PixelBox &pb = buffer->getCurrentLock();
  int stride = (pb.right-pb.left) * 4;
  int height = (pb.bottom-pb.top);
  int length = stride * height;   // Length of the image with alpha
  sws_scale(sws_context, (unsigned char * const*)&pb.data, &stride, 0, height, pic_in.img.plane, pic_in.img.i_stride);
  x264_nal_t* nals;
  int i_nals;
  int outsize = x264_encoder_encode(encoder, &nals, &i_nals, &pic_in, &pic_out);
  if (outsize > 0) {
    bytes += outsize;
    count++;
    std::cout << "frame " << count << " size: " << length << " comp: " << outsize << " kbps: " << (25*8*bytes)/(count*1024) <<"\n";
    write(fd, nals[0].p_payload, outsize);
  }
  //int outsize = gzwrite(gzf, pb.data, length);
  buffer->unlock();
}

